home *** CD-ROM | disk | FTP | other *** search
- /* File MSNTCP.C
- * Main TCP protocol code
- *
- * Copyright (C) 1991, University of Waterloo.
- * Copyright (C) 1985, 1993, Trustees of Columbia University in the
- * City of New York. Permission is granted to any individual or institution
- * to use this software as long as it is not sold for profit. This copyright
- * notice must be retained. This software may not be included in commercial
- * products without written permission of Columbia University.
- *
- * Original version created by Erick Engelke of the University of
- * Waterloo, Waterloo, Ontario, Canada.
- * Adapted and modified for MS-DOS Kermit by Joe R. Doupnik,
- * Utah State University, jrd@cc.usu.edu, jrd@usu.Bitnet.
- *
- * Last edit
- * 18 Feb 1993 v3.13
- *
- * PCTCP - the true worker of Waterloo TCP
- * - contains all opens, closes, major read/write routines and
- * basic IP handler for incomming packets
- * - NOTE: much of the TCP/UDP/IP layering is done at data structure
- * level, not in separate routines or tasks
- *
- */
-
- #include "msntcp.h"
- #include "msnlib.h"
-
- extern int arp_handler(in_Header *);
- extern int rarp_handler(void *);
- extern int icmp_handler(void *);
- extern void icmp_noport(void *);
- extern void server_hello(tcp_Socket *s);
- extern void eth_release(void);
- extern void icmp_init(void);
- extern int eth_init(void);
-
- static int tcp_handler(void *);
- static int tcp_rst(in_Header *, tcp_Header *);
- static int tcp_read(void *, byte FAR *, int);
- static int tcp_write(void *, byte FAR *, int);
- static int tcp_flush(void *);
- static int tcp_close(void *);
- static int tcp_processdata(void *, void *, int);
- static int tcp_send(void *);
- static int tcp_sendsoon(void *);
- static int tcp_retransmitter(void);
- static int tcp_unthread(void *);
- static int udp_handler(void *);
- static int udp_read(void *, byte FAR *, int);
- static int udp_write(void *, byte FAR *, int);
- static int udp_close(udp_Socket *);
-
- extern word pktdevclass, MAC_len; /* MAC address length, for ODI */
- longword ipbcast = 0xffffffffL; /* default IP broadcast address */
-
- static initialized = 0;
- #define DEF_WAITTIME 4 /* default time to hold pkt before sending,
- in PC Bios clock ticks (18.2/sec) */
- static word normal_waittime; /* tcp_sendsoon, nominal waiting time */
- static word waittime; /* tcp_sendsoon, Bios clocks ticks to wait */
-
- #ifdef DEBUG
- void (*dbugxmit)() = NULL;
- void (*dbugrecv)() = NULL;
- #endif
- /*
- * Local IP address
- */
-
- longword my_ip_addr = 0L; /* for external references */
- longword sin_mask = 0xfffffe00L;
- longword sin_gate = 0L;
- byte hostname[MAX_STRING+1]; /* our full name, from BootP */
- /*
- * IP identification numbers
- */
-
- static int ip_id = 0; /* packet number */
- static int next_tcp_port = 1024; /* auto incremented */
- static int next_udp_port = 1024;
- static tcp_Socket *tcp_allsocs = NULL; /* TCP socket linked list head */
- static udp_Socket *udp_allsocs = NULL; /* UDP socket linked list head */
-
- /* Timer definitions, in PC clock tics */
- #define RETRAN_STRAT_TIME 1 /* ticks, how often to check retransmitter*/
- #define tcp_LONGTIMEOUT 31 /* timeout, sec, for opens */
- #define tcp_TIMEOUT 13 /* timeout, sec, during a connection */
-
- #define KARN_NEW 0 /* Karn count, new transmission */
- #define KARN_PENDING 1 /* Karn count, pending transmission */
- #define KARN_TIMEOUT 2 /* Karn count, transmission timed out */
-
- word debug_on = 0;
- word mss = ETH_MSS; /* Maximum Segment Size */
- word sourcequench = 0; /* non-zero if received ICMP Source Quench */
-
- /* TCP Header (and size in bits)
- Source Port (16)
- Destination Port (16)
- Sequence Number (32) First octet in this segment. If SYN then
- this is the initial seq num and first data
- octet is one larger.
- Acknowledgment Number (32) Next octet expected to be received
- Data Offset (4) number of 32 bit words in this header so we
- know when options are present (and how large)
- Reserved (6)
- URG (1) Urgent
- ACK (1) Acknowledgment
- PSH (1) Push function (expedite)
- RST (1) Reset the connection
- SYN (1) Synchronize sequence numbers
- FIN (1) Finish, no more data from sender
- Window (16) Octets willing to accept after Ack value
- Checksum (16) One's complement of header and Pseudo header
- Urgent Pointer (16) Points to octet following urgent data <sic>
- Options & Padding (32) Only Option is max segment size
- Data (the rest)
- */
- /*
- * tcp_init - Initialize the tcp implementation
- * - may be called more than once without hurting
- */
- int
- tcp_init(void)
- {
- extern int arp_last_gateway;
- extern int last_nameserver;
-
- if (initialized != 0) return (1); /* success, inited already */
- /* initialize ethernet interface */
- icmp_init(); /* clear ICMP material */
- arp_last_gateway = 0; /* clear old gateway info */
- last_nameserver = 0; /* reset the nameserver table */
- tcp_allsocs = NULL; /* zero socket pointers */
- udp_allsocs = NULL;
- ip_id = 0; /* packet number, convenient */
- initialized = eth_init(); /* init the hardware, can fail */
- if (my_ip_addr == 0L) /* my_ap_addr is longword*/
- bcopy(ð_addr[2], &my_ip_addr, 4); /* a pseudo long */
- /* next port numbers to choose */
- next_udp_port = next_tcp_port = 1024 + (int)(realclock() & 0x1ff);
- eth_free (NULL); /* clear all pkt rcv bufs */
- if ((pktdevclass == PD_SLIP) ||
- (pktdevclass == PD_ETHER && MAC_len == 0))
- normal_waittime = 4 * DEF_WAITTIME; /* slow down */
- else
- normal_waittime = DEF_WAITTIME;
- return (initialized); /* success (1) or failure (0) */
- }
-
- /*
- * Shut down the card and all services
- */
- void
- tcp_shutdown(void)
- {
- while (tcp_allsocs != NULL)
- tcp_abort(tcp_allsocs);
- eth_release();
- initialized = 0;
- }
-
- int
- udp_open(udp_Socket *s, word lport, longword ina, word port)
- {
- if (s == NULL) return (0);
-
- if (s->rdata) free(s->rdata); /* free any allocated buffer */
- memset(s, 0, sizeof(udp_Socket));
- s->rdata = malloc(UDP_BUFSIZE);
- s->ip_type = UDP_PROTO;
- if (lport == 0) lport = ++next_udp_port; /* get a nonzero port val */
- s->myport = lport;
-
- /* check for broadcast */
- if (ina == 0xffffffffL || ina == 0L || ina == ipbcast)
- memset(s->hisethaddr, 0xff, sizeof(eth_address));
- else
- if (arp_resolve(ina, &s->hisethaddr[0]) == 0)
- return (0);
- s->hisaddr = ina;
- s->hisport = port;
- s->next = udp_allsocs;
- udp_allsocs = s;
- s->sisopen = SOCKET_OPEN;
- return (1);
- }
-
- /*
- * Actively open a TCP connection to a particular destination.
- * - 0 on error
- */
- int
- tcp_open(tcp_Socket *s, word lport, longword ina, word port)
- {
- if (s == NULL) return (0);
-
- if (s->rdata) free(s->rdata); /* free preallocated buf */
- if (s->sdata) free(s->sdata); /* free preallocated buf */
-
- memset(s, 0, sizeof(tcp_Socket)); /* zero everything */
- s->ip_type = TCP_PROTO;
- if ((ina ^ my_ip_addr) & sin_mask) /* if not on this network */
- mss = (mss > 536)? 536: mss; /* non-fragmentable max */
- s->mss = mss;
- s->state = tcp_StateSYNSENT;
- s->timeout = set_timeout(tcp_LONGTIMEOUT);
- s->rmaxdatalen = TCP_RBUFSIZE;
- s->cwindow = 1; /* slow start VJ algorithm */
- s->vj_sa = 4; /* about 250 ms (4/18.2 tics/sec) */
- if (lport == 0)
- lport = ++next_tcp_port; /* get a nonzero port value */
- s->myport = lport;
- if (! arp_resolve(ina, &s->hisethaddr[0]))
- return (0); /* failed to get host Ethernet addr */
- s->hisaddr = ina;
- s->hisport = port;
- s->seqnum = ntohl(set_timeout(0)) & 0xffff0000;
- s->sdatalen = 0;
- s->flags = tcp_FlagSYN;
- s->rptxmit = TRUE;
- s->next = tcp_allsocs;
- s->rtt_delay = s->rtt_smooth = 18; /* one second startup */
- tcp_allsocs = s;
- s->rdata = malloc(TCP_RBUFSIZE); /* create data buffers */
- s->sdata = malloc(TCP_SBUFSIZE);
- s->sisopen = SOCKET_OPEN;
- return (tcp_send(s)); /* fail if send fails */
- }
-
- /*
- * Passive open: listen for a connection on a particular port
- */
- tcp_listen(tcp_Socket *s, word lport, longword ina, word port, word timeout)
- {
- if (s == NULL) return (0);
-
- if (s->rdata) free(s->rdata); /* free preallocated buf */
- if (s->sdata) free(s->sdata); /* free preallocated buf */
- memset(s, 0, sizeof(tcp_Socket));
- s->ip_type = TCP_PROTO;
- s->mss = mss;
- s->rmaxdatalen = TCP_RBUFSIZE;
- s->cwindow = 1; /* slow start VJ algorithm */
- s->vj_sa = 4; /* about 250 ms */
- s->state = tcp_StateLISTEN;
- if (timeout == 0)
- s->timeout = 0; /* no timeout */
- else
- s->timeout = set_timeout(timeout);
- s->myport = lport;
- s->hisport = port;
- s->hisaddr = ina;
- s->seqnum = ntohl(set_timeout(0)) & 0xffff0000;
- s->sdatalen = 0;
- s->flags = 0;
- s->rptxmit = FALSE;
- s->next = tcp_allsocs;
- tcp_allsocs = s;
- s->rdata = malloc(TCP_RBUFSIZE); /* create data buffers */
- s->sdata = malloc(TCP_SBUFSIZE);
- s->sisopen = SOCKET_OPEN;
- if ((pktdevclass == PD_SLIP) ||
- (pktdevclass == PD_ETHER && MAC_len == 0))
- normal_waittime = 4 * DEF_WAITTIME; /* slow down */
- else
- normal_waittime = DEF_WAITTIME;
- return (1);
- }
-
-
- static int
- udp_close(udp_Socket *ds)
- {
- register udp_Socket *sp;
-
- sp = udp_allsocs; /* ptr to first socket */
- if (sp == NULL) return (0); /* failure */
-
- ds->sisopen = SOCKET_CLOSED; /* close before doing other work */
- ds->rdatalen = 0; /* flush pending reads */
- if (sp == ds)
- udp_allsocs = ds->next; /* if we are first, unlink */
- while (sp != NULL)
- {
- if (sp->next == ds) /* if current points to us */
- {
- sp->next = ds->next; /* point it to our successor */
- break;
- }
- sp = sp->next; /* look at next socket */
- }
- free(ds->rdata); /* free data buffer */
- ds->rdata = NULL; /* invalidate data buffer address */
- return (1); /* success */
- }
-
- /*
- * Send a FIN on a particular port -- only works if it is open
- * Must still allow receives
- * Returns 0 for failure
- */
- static int
- tcp_close(tcp_Socket *s)
- {
- if (s == NULL || s->ip_type != TCP_PROTO)
- return (0); /* failure */
- if (s->state == tcp_StateESTAB || s->state == tcp_StateSYNREC)
- {
- if (s->err_msg == NULL)
- s->err_msg = "TCP_Closed called";
- s->flags = tcp_FlagACK | tcp_FlagFIN;
- if (s->sdatalen)
- s->flags |= tcp_FlagPUSH;
- s->state = tcp_StateFINWT1;
- s->timeout = set_timeout(4);
- /* should be a pretty lengthy time */
- s->rptxmit = TRUE;
- tcp_send(s);
- return (1); /* success */
- }
- return (0);
- }
-
- /*
- * Abort a tcp connection
- * Returns 0 if failure
- */
- int
- tcp_abort(tcp_Socket *s)
- {
- if (s == NULL) return (0); /* failure */
-
- if (s->err_msg == NULL) s->err_msg = "TCP_ABORT";
- if ((s->state != tcp_StateLISTEN) && (s->state != tcp_StateCLOSED))
- {
- s->flags = tcp_FlagRST | tcp_FlagACK;
- tcp_send(s);
- }
- s->rptxmit = FALSE;
- s->sdatalen = 0;
- s->state = tcp_StateCLOSED;
- tcp_unthread(s);
- return (1); /* success */
- }
-
- /*
- * tcp_sendsoon - schedule a transmission pretty soon
- * - this one has an imperfection at midnight, but it
- * is not significant to the connection performance
- * Return 0 if failure.
- */
- int
- tcp_sendsoon(tcp_Socket *s)
- {
- register longword temp;
-
- if (s == NULL) return (0);
-
- if (s->ip_type != TCP_PROTO) return (0);
-
- s->karn_count = KARN_PENDING;
- if (sourcequench != 0) /* ICMP shutup message */
- {
- waittime += waittime; /* double the timeout */
- sourcequench = 0; /* forget the reminder */
- }
-
- temp = set_ttimeout(waittime); /* Bios ticks into the future */
- if ((s->rtt_time < temp)) /* if timeout is in future */
- return (1); /* then wait some more */
- if (waittime > normal_waittime) /* if backing off already */
- waittime = (waittime * 15) / 16; /* recover slowly */
- s->rtt_time = temp; /* set timeout of this pkt */
- return (1);
- }
-
- /*
- * Retransmitter - called periodically to perform tcp retransmissions
- * Returns 0 if failure
- */
- static int
- tcp_retransmitter(void)
- {
- register tcp_Socket *s;
- static longword retran_strat = 0L; /* timeout retran strategy */
-
- /* do this only once per RETRAN_STRAT_TIME clock ticks */
- if (!chk_timeout(retran_strat))
- return (0L);
- retran_strat = set_ttimeout(RETRAN_STRAT_TIME);
-
- for (s = tcp_allsocs; s != NULL; s = s->next)
- {
- if ((s->sdatalen > 0) || (s->rptxmit == TRUE) ||
- (s->karn_count == KARN_PENDING))
- /* retransmission strategy */
- if (chk_timeout(s->rtt_time)) /* if timeout */
- {
- if (s->karn_count == KARN_NEW)
- s->karn_count = KARN_TIMEOUT;
- /* if really did timeout */
- tcp_send(s);
- }
-
- if (s->timeout && chk_timeout(s->timeout))
- {
- if (s->state == tcp_StateTIMEWT)
- {
- s->flags = tcp_FlagACK;
- s->state = tcp_StateCLOSED;
- tcp_unthread(s); /* purge it */
- }
- else if (s->state != tcp_StateESTAB)
- {
- s->err_msg = "Timeout, aborting";
- tcp_abort(s);
- }
- }
- }
- return (1); /* success */
- }
-
- /*
- * Unthread a socket from the socket list, if it's there. Return 0 on failure
- */
- static int
- tcp_unthread(tcp_Socket *ds)
- {
- register tcp_Socket *sp;
-
- if (ds == NULL) return (0); /* failure */
-
- if ((ds->rdatalen == 0) || (ds->state > tcp_StateESTAB))
- ds->sisopen = SOCKET_CLOSED; /* ok to close socket */
- ds->state = tcp_StateCLOSED;
-
- if ((sp = tcp_allsocs) == NULL) /* no socket in the queue */
- return (0);
-
- if (sp == ds)
- tcp_allsocs = ds->next; /* if we are first, unlink */
- while (sp != NULL)
- {
- if (sp->next == ds) /* if current points to us */
- {
- sp->next = ds->next; /* point it to our successor */
- break;
- }
- sp = sp->next; /* look at next socket */
- }
- return (1); /* success */
- }
-
- /*
- * tcp_tick - called periodically by user application
- * - returns 0 when our socket closes
- * - called with socket parameter or NULL
- */
- tcp_tick(sock_type *s)
- {
- register in_Header *ip;
- int packettype;
- /* read a packet */
- while ((ip = (in_Header *)eth_arrived(&packettype)) != NULL)
- {
- switch (packettype) /* network big endian form */
- {
- case TYPE_IP: /* do IP */
- if ((ip->hdrlen_ver != 0x45) ||
- (checksum(ip, in_GetHdrlenBytes(ip)) != 0xffff))
- {
- if (debug_on)
- outs("IP Received BAD Checksum \n\r");
- break;
- }
- if ((my_ip_addr == 0L) ||
- (htonl(ip->destination) == my_ip_addr))
- {
- switch (ip->proto)
- {
- case TCP_PROTO:
- tcp_handler(ip);
- break;
- case UDP_PROTO:
- udp_handler(ip);
- break;
- case ICMP_PROTO:
- icmp_handler(ip);
- break;
- default:
- break;
- } /* end of switch (ip->proto) */
- }
- break;
-
- case TYPE_ARP: /* do ARP */
- arp_handler(ip);
- break;
- case TYPE_RARP: /* do RARP */
- rarp_handler(ip);
- break;
- default: /* unknown type */
- break;
- } /* end of switch */
- eth_free(ip); /* free the processed packet */
- } /* end of while */
-
- tcp_retransmitter(); /* check on pending sends */
- return ((s != NULL)? s->tcp.sisopen: 0); /* 0 means closed socket */
- }
-
- void
- tcp_set_debug_state(word x)
- {
- debug_on = x;
- }
-
- /* returns 1 if connection is established, else 0 */
- int
- tcp_established(tcp_Socket *s)
- {
- if (s == NULL) return (0); /* failure */
- return (s->state == tcp_StateESTAB);
- }
-
-
- static int
- udp_write(udp_Socket *s, byte FAR *datap, int len)
- {
- tcp_PseudoHeader ph;
- struct pkt
- {
- in_Header in;
- udp_Header udp;
- int data;
- } register *pkt;
-
- if (s == NULL || datap == NULL)
- return (0); /* failure */
-
- pkt = (struct pkt *)eth_formatpacket(&s->hisethaddr[0], TYPE_IP);
- pkt->in.length = htons(sizeof(in_Header) + UDP_LENGTH + len);
- /* UDP header */
- pkt->udp.srcPort = htons(s->myport);
- pkt->udp.dstPort = htons(s->hisport);
- pkt->udp.length = htons(UDP_LENGTH + len);
- bcopyff(datap, &pkt->data, len);
- /* Internet header */
- pkt->in.hdrlen_ver = 0x45; /* version 4, hdrlen 5 */
- pkt->in.tos = 0;
- pkt->in.identification = htons(++ip_id); /* was post inc */
- pkt->in.frag = 0;
- #ifdef KERMIT
- pkt->in.ttl = 60;
- #else
- pkt->in.ttl = 254;
- #endif
- pkt->in.proto = UDP_PROTO; /* UDP */
- pkt->in.checksum = 0;
- pkt->in.source = htonl(my_ip_addr);
- pkt->in.destination = htonl(s->hisaddr);
- pkt->in.checksum = ~checksum(&pkt->in, sizeof(in_Header));
- /* compute udp checksum if desired */
- if (s->soc_mode & UDP_MODE_NOCHK)
- pkt->udp.checksum = 0;
- else
- {
- ph.src = pkt->in.source; /* big endian now */
- ph.dst = pkt->in.destination;
- ph.mbz = 0;
- ph.protocol = UDP_PROTO; /* UDP */
- ph.length = pkt->udp.length; /* big endian now */
- ph.checksum = checksum(&pkt->udp, htons(ph.length));
- pkt->udp.checksum = ~checksum(&ph, sizeof(ph));
- }
-
- #ifdef DEBUG
- if (dbugxmit) (*dbugxmit)(s,inp,udpp);
- #endif
- if (eth_send(ntohs(pkt->in.length)) != 0) /* send pkt */
- return (len);
- else return (0); /* failed */
- }
-
- /*
- * udp_read - read data from buffer, does large buffering.
- * Return 0 on failure.
- */
- static int
- udp_read(udp_Socket *s, byte FAR * datap, int maxlen)
- {
- register int x;
-
- if (s == NULL || datap == NULL || maxlen ==0)
- return (0); /* failure */
- if ((x = s->rdatalen) > 0)
- {
- if (x > maxlen) x = maxlen;
- bcopyff(s->rdata, datap, x);
- if (s->rdatalen -= x)
- bcopyff(&s->rdata[x], s->rdata, s->rdatalen);
- }
- return (x);
- }
-
- udp_cancel(in_Header *ip)
- {
- int len;
- register udp_Header *up;
- register udp_Socket *s;
- /* match to a udp socket */
- len = in_GetHdrlenBytes(ip);
- up = (udp_Header *)((byte *)ip + len); /* udp frame pointer */
- /* demux to active sockets */
- for (s = udp_allsocs; s != NULL; s = s->next)
- {
- if (s->hisport != 0 &&
- ntohs(up->dstPort) == s->myport &&
- ntohs(up->srcPort) == s->hisport &&
- ntohl(ip->source) == s->hisaddr)
- break;
- if (s->hisport != 0 && /* ICMP repeat of our pkt */
- ntohs(up->dstPort) == s->hisport &&
- ntohs(up->srcPort) == s->myport &&
- ntohl(ip->source) == my_ip_addr)
- break;
- }
-
- if (s == NULL) /* demux to passive sockets */
- for (s = udp_allsocs; s != NULL; s = s->next)
- if (s->hisport == 0 &&
- ntohs(up->dstPort) == s->myport)
- break;
-
- if (s != NULL)
- udp_close(s);
- return (1); /* success */
- }
-
- int
- tcp_cancel(in_Header *ip)
- {
- int len;
- register tcp_Socket *s;
- register tcp_Header *tp;
-
- if (ip == NULL) return (0); /* failure */
- len = in_GetHdrlenBytes(ip); /* check work */
-
- tp = (tcp_Header *)((byte *)ip + len); /* TCP frame pointer */
- /* demux to active sockets */
- for (s = tcp_allsocs; s != NULL; s = s->next)
- {
- if (s->hisport != 0 && /* them to us */
- ntohs(tp->dstPort) == s->myport &&
- ntohs(tp->srcPort) == s->hisport &&
- ntohl(ip->source) == s->hisaddr)
- break;
- if (s->hisport != 0 && /* ICMP repeat of our pkt */
- ntohs(tp->dstPort) == s->hisport &&
- ntohs(tp->srcPort) == s->myport &&
- ntohl(ip->source) == my_ip_addr)
- break;
- }
-
- if (s == NULL) /* demux to passive sockets */
- for (s = tcp_allsocs; s != NULL; s = s->next)
- if (s->hisport == 0 &&
- ntohs(tp->dstPort) == s->myport)
- break;
-
- if (s != NULL)
- {
- s->rdatalen = 0;
- s->state = tcp_StateCLOSED;
- tcp_unthread(s);
- }
- return (1); /* success */
- }
-
- static int
- tcp_read(tcp_Socket *s, byte FAR *datap, int maxlen)
- {
- register int x;
-
- if (s == NULL || datap == NULL || maxlen <= 0)
- return (0); /* failure or read nothing */
- if ((x = s->rdatalen) > 0)
- {
- if (x > maxlen) x = maxlen;
- bcopyff(s->rdata, datap, x); /* copy out desired data */
- s->rdatalen -= x;
- bcopyff(&s->rdata[x], s->rdata, s->rdatalen);
- tcp_sendsoon(s);
- if (x == maxlen && s->rdatalen <= maxlen) /* last gulp */
- tcp_sendsoon(s); /* schedule window update */
- else /* for SLIP update when space for a new packet */
- if (((pktdevclass == PD_SLIP) || (pktdevclass == PD_ETHER
- && MAC_len == 0)) && (TCP_RBUFSIZE - s->rdatalen > (int) mss))
- tcp_sendsoon(s); /* schedule window update */
- }
- return (x); /* return bytes read */
- }
-
- /*
- * Write data to a connection.
- * Returns number of bytes written, == 0 when connection is not in
- * established state.
- */
- static int
- tcp_write(tcp_Socket *s, byte FAR *dp, int len)
- {
- register int x;
-
- if (s == NULL || dp == NULL) return (0); /* failure */
- if (s->state != tcp_StateESTAB) len = 0;
- if (len > (x = TCP_SBUFSIZE - s->sdatalen)) len = x;
- if (len > 0)
- {
- bcopyff(dp, &s->sdata[s->sdatalen], len); /* append to buf */
- s->sdatalen += len; /* implies sending a packet */
-
- if (s->soc_mode & TCP_MODE_NONAGLE)
- tcp_send(s);
- else /* transmit if first data or reached MTU */
- /* not true MTU, but better than nothing */
- /* Nagle's algorithm, RFC 896 */
- if ((s->sdatalen == len) ||
- (s->sdatalen >= (int)(s->mss)))
- tcp_send(s);
- }
- return (len);
- }
-
- /*
- * Send pending data
- */
- static int
- tcp_flush(tcp_Socket *s)
- {
- if (s == NULL) return (0); /* failure */
- if (s->sdatalen > 0)
- {
- s->flags |= tcp_FlagPUSH;
- return (tcp_send(s));
- }
- return (1); /* success */
- }
-
- /*
- * Handler for incoming UDP packets. If no socket tell the host via ICMP.
- */
- static int
- udp_handler(in_Header *ip)
- {
- register udp_Header *up;
- register udp_Socket *s;
- tcp_PseudoHeader ph;
- byte *dp;
- word len;
-
- len = in_GetHdrlenBytes(ip);
- up = (udp_Header *)((byte *)ip + len); /* UDP segment pointer */
- len = ntohs(up->length);
- /* demux to active sockets */
- for (s = udp_allsocs; s != NULL; s = s->next)
- if (s->sisopen == SOCKET_OPEN && s->hisport != 0 &&
- ntohs(up->dstPort) == s->myport &&
- ntohs(up->srcPort) == s->hisport &&
- ntohl(ip->source) == s->hisaddr)
- break;
-
- #ifdef DEBUG
- if (dbugrecv) (*dbugrecv)(s,ip,up);
- #endif
- if (s == NULL) /* demux to passive sockets */
- for (s = udp_allsocs; s != NULL; s = s->next)
- if (s->sisopen == SOCKET_OPEN && s->hisaddr == 0 &&
- ntohs(up->dstPort) == s->myport)
- {
- if (arp_resolve(htonl(ip->source),
- &s->hisethaddr[0]))
- {
- s->hisaddr = ntohl(ip->source);
- s->hisport = ntohs(up->srcPort);
- }
- break;
- }
-
- if (s == NULL) /* demux to broadcast sockets */
- for (s = udp_allsocs; s != NULL; s = s->next)
- if (s->sisopen == SOCKET_OPEN &&
- s->hisaddr == ipbcast &&
- ntohs(up->dstPort) == s->myport)
- break;
-
- if (s == NULL)
- {
- icmp_noport(ip); /* tell host port is unreachable */
- if (debug_on) outs(" discarding...");
- return (0); /* say no socket */
- }
-
- if (up->checksum)
- {
- ph.src = ip->source; /* already bigend'd */
- ph.dst = ip->destination;
- ph.mbz = 0;
- ph.protocol = UDP_PROTO;
- ph.length = up->length;
- ph.checksum = checksum(up, len);
- if (checksum(&ph, sizeof(tcp_PseudoHeader)) != 0xffff)
- return (0); /* failure */
- }
- /* process user data */
- if ((len -= UDP_LENGTH) > 0)
- {
- dp = (byte *)(up);
- if (len > UDP_BUFSIZE) len = UDP_BUFSIZE;
- bcopyff(&dp[UDP_LENGTH], s->rdata, len); /* write to buf */
- s->rdatalen = len;
- bcopyff(&ip->source, &s->hisaddr, 6); /* sender's IP */
- }
- return (1); /* success */
- }
-
- /* Handle TCP packets. If no socket send an RST pkt. */
-
- static
- tcp_handler(in_Header *ip)
- {
- register tcp_Header *tp;
- register tcp_Socket *s;
- tcp_PseudoHeader ph;
- int len, diff; /* signed, please */
- word flags;
- long diffticks, ldiff; /* must be signed */
-
- len = in_GetHdrlenBytes(ip);
- tp = (tcp_Header *)((byte *)ip + len); /* tcp frame pointer */
- len = ntohs(ip->length) - len; /* len of tcp data */
- flags = ntohs(tp->flags); /* flags from pkt */
-
- /* demux to active sockets */
- for (s = tcp_allsocs; s != NULL; s = s->next)
- if (s->hisport != 0 &&
- ntohs(tp->dstPort) == s->myport &&
- ntohs(tp->srcPort) == s->hisport &&
- ntohl(ip->source) == s->hisaddr)
- break;
-
- if (s == NULL) /* demux to passive sockets, must be a new session */
- for (s = tcp_allsocs; s != NULL; s = s->next)
- if ((s->hisport == 0) &&
- (ntohs(tp->dstPort) == s->myport))
- break;
-
- #ifdef DEBUG
- if (dbugrecv) (*dbugrecv)(s, ip, tp);
- #endif
-
- if (s == NULL)
- {
- /* no session seems to exist, nor is there desire to start
- one, so we must send a reset */
- tcp_rst(ip, tp);
- return (0); /* 0 to say socket is closed */
- }
-
- /* save remote Ethernet address to socket */
- bcopy(&((((eth_Header *)ip) - 1)->source[0]), &s->hisethaddr[0],
- sizeof(eth_address));
-
- ph.src = ip->source; /* network order now */
- ph.dst = ip->destination;
- ph.mbz = 0;
- ph.protocol = TCP_PROTO;
- ph.length = htons(len);
- ph.checksum = checksum(tp, len);
- if (checksum(&ph, sizeof(ph)) != 0xffff)
- {
- if (debug_on) outs("bad tcp checksum \n\r");
- return (1);
- }
-
- /* reset code */
- if (flags & tcp_FlagRST)
- {
- if (debug_on) outs("\7\7connection reset\n");
- s->rdatalen = 0;
- s->state = tcp_StateCLOSED;
- tcp_unthread(s);
- return (0); /* say socket is closed */
- }
-
- /* update our retransmission stuff */
- if (s->karn_count != KARN_TIMEOUT)
- if ((diffticks = set_ttimeout(0) - s->vj_last) >= 0)
- { /* we ignore the overnight case */
- diffticks -= (s->vj_sa >> 3);
- s->vj_sa += (word)diffticks;
- if (diffticks < 0)
- diffticks = -diffticks;
- diffticks -= (s->vj_sd >> 2);
- s->vj_sd += (word)diffticks;
- s->rto = ((s->vj_sa >> 2) + s->vj_sd) >> 1;
- s->karn_count = KARN_NEW;
- } /* else (== KARN_TIMEOUT) use the backed off rto */
-
- s->rptxmit = FALSE;
-
- switch (s->state) {
-
- case tcp_StateLISTEN: /* accepting SYNs */
- if (flags & tcp_FlagSYN)
- {
- s->acknum = ntohl(tp->seqnum) + 1;
- s->hisport = ntohs(tp->srcPort);
- s->hisaddr = ntohl(ip->source);
- s->flags = tcp_FlagSYN | tcp_FlagACK;
- server_hello(s); /* put greeting msg in socket */
- tcp_send(s); /* we must respond immediately */
- s->state = tcp_StateSYNREC;
- s->timeout = set_timeout(tcp_TIMEOUT);
- }
- else
- tcp_rst(ip, tp); /* send a reset */
- break;
-
- case tcp_StateSYNSENT: /* added ACK Section */
- if (flags & tcp_FlagSYN)
- {
- s->flags = tcp_FlagACK;
- s->timeout = set_timeout(tcp_TIMEOUT);
-
- /* FlagACK means connection established, else SYNREC */
- if (flags & tcp_FlagACK)
- { /* but is it for the correct session? */
- if (tp->acknum == htonl(s->seqnum + 1))
- {
- s->state = tcp_StateESTAB;
- s->seqnum++; /* good increment */
- s->acknum = ntohl(tp->seqnum) + 1; /*32 bits*/
- tcp_processdata(s, tp, len);
- tcp_sendsoon(s); /* reply */
- }
- else
- { /* wrong ack, force a RST and resend SYN */
- s->flags = tcp_FlagRST;
- tcp_send(s);
- s->flags = tcp_FlagSYN;
- tcp_send(s);
- }
- }
- else
- {
- s->acknum++;
- s->state = tcp_StateSYNREC;
- /* maybe check sequence number stuff too */
- }
- }
- break;
-
- case tcp_StateSYNREC: /* recSYNSENT, sentACK, waiting EST */
- if (flags & tcp_FlagSYN)
- {
- s->flags = tcp_FlagSYN | tcp_FlagACK;
- tcp_send(s);
- s->timeout = set_timeout(tcp_TIMEOUT);
- }
- if ((flags & tcp_FlagACK) && (ntohl(tp->acknum) == (s->seqnum + 1)))
- {
- s->flags = tcp_FlagACK;
- tcp_send(s);
- s->seqnum++;
- s->state = tcp_StateESTAB;
- s->timeout = 0; /* never timeout */
- tcp_processdata(s, tp, len);
- }
- break;
-
- case tcp_StateESTAB:
- if ((flags & tcp_FlagACK) == 0) return (1); /* they must ack somthing*/
-
- s->timeout = 0L; /* we do not timeout at this point */
- /* process their ack value in packet */
- /* If their ack preceeds the window this is an old packet. */
- /* If ack exceeds the window then they are lying, don't believe. */
- /* However, in all cases grab any useful data for us. */
- ldiff = ntohl(tp->acknum) - s->seqnum; /* current - prev ack */
- if (ldiff >= 0 && ldiff <= s->sdatalen)
- { /* their ack is in our window*/
- s->seqnum += ldiff; /* update ACK'd file pointer */
- diff = (int)ldiff; /* 16 bits, bigger than our window */
- s->sdatalen -= diff; /* deduct amount ACK'd */
- /* move down residual in send buf */
- bcopyff(&s->sdata[diff], s->sdata, s->sdatalen);
- }
- s->flags = tcp_FlagACK; /* tell them thanks */
- tcp_processdata(s, tp, len); /* process our data in the packet */
-
- if (ldiff != 0 || len != 0)
- tcp_sendsoon(s); /* ACK to maybe our closed window */
- break;
-
- case tcp_StateFINWT1:
- /* process ack value in packet */
- ldiff = ntohl(tp->acknum) - s->seqnum; /* current - prev ack */
- if (ldiff >= 0 && ldiff <= s->sdatalen) /* their ack is in our window*/
- {
- s->seqnum += ldiff; /* update ACK'd file counter */
- diff = (int)ldiff; /* 16 bits, more than our window */
- s->sdatalen -= diff; /* deduct amount ACK'd */
- bcopyff(&s->sdata[diff], s->sdata, s->sdatalen);
- /* move residual */
- }
- tcp_processdata(s, tp, len); /* process our data in the packet */
- tcp_send(s);
-
- if (flags & tcp_FlagFIN) /* trying to do simultaneous close */
- {
- if ((ntohl(tp->acknum) >= s->seqnum + 1) &&
- (ntohl(tp->seqnum) == s->acknum))
- { /* other guy wishes to close too */
- s->seqnum++;
- s->acknum++;
- s->flags = tcp_FlagACK;
- s->rptxmit = TRUE;
- s->timeout = set_timeout(3);
- s->state = tcp_StateCLOSING;
- tcp_send(s);
- }
- }
- else if (flags & tcp_FlagACK)
- /* other side is legitimately acking our fin */
- if ((ntohl(tp->acknum) == s->seqnum + 1) &&
- (ntohl(tp->seqnum) == s->acknum) &&
- (s->sdatalen == 0))
- {
- s->seqnum++;
- s->sdatalen = 0;
- s->state = tcp_StateFINWT2;
- s->timeout = set_timeout(3);
- }
- break;
-
- case tcp_StateFINWT2: /* we can timeout of this state */
- if ((flags & (tcp_FlagACK | tcp_FlagFIN)) ==
- (tcp_FlagACK | tcp_FlagFIN))
- if ((ntohl(tp->acknum) == s->seqnum) &&
- (ntohl(tp->seqnum) == s->acknum))
- {
- s->acknum++;
- s->flags = tcp_FlagACK;
- tcp_send(s);
- s->timeout = set_timeout(2); /* max seg life 2 sec */
- s->state = tcp_StateTIMEWT;
- }
- break;
-
- case tcp_StateCLOSING:
- if ((flags & (tcp_FlagACK | tcp_FlagFIN)) == tcp_FlagACK)
- if ((tp->acknum == htonl(s->seqnum)) &&
- (tp->seqnum == htonl(s->acknum))) /* all agree */
- {
- s->state = tcp_StateTIMEWT;
- s->timeout = set_timeout(tcp_TIMEOUT);
- }
- break;
-
- case tcp_StateLASTACK:
- if (flags & tcp_FlagFIN) /* they lost our two packets, back up */
- {
- s->flags = tcp_FlagACK;
- tcp_send(s);
- s->flags = tcp_FlagACK | tcp_FlagFIN;
- tcp_send(s);
- }
- else
- {
- if ((ntohl(tp->acknum) == (s->seqnum + 1)) &&
- (ntohl(tp->seqnum) == s->acknum))
- {
- s->state = tcp_StateCLOSED; /* no 2 msl */
- tcp_unthread(s);
- }
- return (1);
- }
- break;
-
- case tcp_StateTIMEWT:
- s->flags = tcp_FlagACK;
- s->acknum = ntohl(tp->seqnum) + 1;
- tcp_send(s);
- break;
- } /* end switch */
- return (1); /* success */
- }
-
- /*
- * Process the data in an incoming packet.
- * Called from all states where incoming data can be received: established,
- * fin-wait-1, fin-wait-2
- */
- static int
- tcp_processdata(tcp_Socket *s, tcp_Header *tp, int len)
- {
- register int diff, x;
- long ldiff; /* signed */
- byte *dp;
- word flags, *options, numoptions, opt_temp;
-
- if (s == NULL || tp == NULL) return (0); /* failure */
-
- s->window = ntohs(tp->window);
- if (s->window > 0x7fff) /* SGI 64KB window nonsense?*/
- s->window = 0x7fff; /* yes, cut window to 32KB */
- flags = ntohs(tp->flags);
- ldiff = s->acknum - ntohl(tp->seqnum); /* signed long */
- if (flags & tcp_FlagSYN) ldiff--; /* SYN counts as one unit */
- diff = (int)ldiff; /* 16 bit version */
-
- /* find the data portion */
- x = tcp_GetDataOffset(tp) << 2; /* quadword to byte count */
- dp = (byte *)tp + x; /* points to data */
-
- /* process those options */
- if (numoptions = x - sizeof(tcp_Header))
- {
- options = (word *)(tp + sizeof(tcp_Header));
- while (numoptions-- > 0)
- switch (*options++)
- {
- case 0: numoptions = 0; /* end of options */
- case 1: break; /* nop */
-
- /* we are very liberal on MSS stuff */
- case 2: if (*options == 2)
- {
- opt_temp = ntohs(* (word *)(&options[1]));
- if (opt_temp < s->mss)
- s->mss = opt_temp;
- }
- numoptions -= 2 + *options;
- options += *options;
- break;
- } /* end of switch and while */
- } /* end of if */
- /* done option processing */
-
- len -= x; /* remove the header length */
- if (diff >= 0) /* skip already received bytes */
- {
- dp += diff; /* move to new data */
- len -= diff; /* length of new data */
- /* limit receive size to our window */
- if (s->rdatalen < 0)
- s->rdatalen = 0; /* pointer sanity check */
- if (len > (x = TCP_RBUFSIZE - s->rdatalen))
- len = x; /* space available only */
-
- if (len > 0)
- { /* new ack begins at end of data */
- s->acknum += len;
- bcopyff(dp, &s->rdata[s->rdatalen], len);
- s->rdatalen += len;
- }
- } /* end of if (diff > 0) */
- s->rptxmit = FALSE; /* don't send ACK response yet */
-
- if (flags & tcp_FlagFIN)
- {
- s->acknum++;
- switch (s->state)
- {
- case tcp_StateESTAB:
- s->err_msg = "Connection closed.";
- /* tcp_StateCLOSWT ... here we go */
- x = tcp_StateLASTACK;
- s->flags |= tcp_FlagFIN;
- s->rptxmit = TRUE; /* to send packet */
- break;
-
- case tcp_StateFINWT1: /* on ack should go to finwt2 */
- s->flags |= tcp_FlagFIN;
- /* not certain of this addition */
- x = tcp_StateCLOSING;
- break;
-
- case tcp_StateFINWT2:
- x = tcp_StateTIMEWT;
- break;
- } /* end of switch (s->state) */
- s->state = x;
- } /* end of if (flags & tcp_FlagFIN) */
- s->timeout = set_timeout(tcp_TIMEOUT);
- return (1); /* success */
- }
-
- /*
- * Format and send an outgoing segment
- */
- static int
- tcp_send(tcp_Socket *s)
- {
- tcp_PseudoHeader ph;
- struct pkt
- {
- in_Header in;
- tcp_Header tcp;
- word maxsegopt[2];
- } register *pkt;
- byte *dp;
- int senddatalen, sendtotlen, sendpktlen;
- register byte ippkt; /* 1..s->cwindow */
-
- if (s == NULL) return (0); /* failure */
-
- pkt = (struct pkt *)eth_formatpacket(&s->hisethaddr[0], TYPE_IP);
- dp = (byte *)pkt->maxsegopt; /* dp constant for multi-packet sends */
-
- /* this is our total possible send size */
- if (s->window > 0)
- senddatalen = (s->sdatalen >= (int)s->window)?
- s->window - 1: s->sdatalen;
- else senddatalen = 0;
-
- sendtotlen = 0; /* running count of what we've sent */
-
- /* step through our packets */
- for (ippkt = 1; ippkt <= s->cwindow; ippkt++)
- { /* adjust size for each packet */
- if (senddatalen > (int)s->mss)
- senddatalen = (int)s->mss;
- /* tcp header */
- pkt->tcp.srcPort = htons(s->myport);
- pkt->tcp.dstPort = htons(s->hisport);
- pkt->tcp.seqnum = htonl(s->seqnum + sendtotlen);
- pkt->tcp.acknum = htonl(s->acknum);
- pkt->tcp.window = htons(s->rmaxdatalen - s->rdatalen);
- pkt->tcp.flags = htons(s->flags | 0x5000);
- pkt->tcp.checksum = 0;
- pkt->tcp.urgentPointer = 0;
-
- /* do options if this is our first packet */
- if ((s->flags & (tcp_FlagSYN | tcp_FlagACK)) == tcp_FlagSYN)
- {
- sendpktlen = sizeof(tcp_Header) +
- sizeof(in_Header) + 4;
- pkt->tcp.flags = htons(ntohs(pkt->tcp.flags) + 0x1000);
- pkt->maxsegopt[0] = 0x0402;
- pkt->maxsegopt[1] = htons(s->mss);
- dp += 4;
- }
- else
- {
- /* handle no-data, not-first-SYN packets */
- sendpktlen = sizeof(tcp_Header) + sizeof(in_Header);
- if (senddatalen > 0) /* handle packets with data */
- {
- sendpktlen += senddatalen;
- bcopyff(&s->sdata[sendtotlen], dp, senddatalen);
- }
- }
-
- /* Internet header */
- pkt->in.hdrlen_ver = 0x45; /* version 4, hdrlen 5 */
- pkt->in.tos = 0; /* crummy service is ok */
- pkt->in.identification = htons(++ip_id); /* pre-inc req'd */
- pkt->in.frag = 0; /* we can't do fragments */
- #ifdef KERMIT
- pkt->in.ttl = 60; /* seconds */
- #else
- pkt->in.ttl = 254;
- #endif
- pkt->in.proto = TCP_PROTO;
- pkt->in.checksum = 0;
- pkt->in.source = htonl(my_ip_addr);
- pkt->in.destination = htonl(s->hisaddr);
- pkt->in.length = htons(sendpktlen);
-
- pkt->in.checksum = ~checksum(&pkt->in, sizeof(in_Header));
-
- /* compute tcp checksum */
- ph.src = pkt->in.source; /* already in network order */
- ph.dst = pkt->in.destination;
- ph.mbz = 0;
- ph.protocol = TCP_PROTO;
- ph.length = htons(sendpktlen - sizeof(in_Header));
- ph.checksum = checksum(&pkt->tcp,
- (sendpktlen - sizeof(in_Header) + 1) & 0xfffe);
- pkt->tcp.checksum = ~checksum(&ph, sizeof(ph));
-
- #ifdef DEBUG
- if (dbugxmit) (*dbugxmit)(s, &pkt->in, &pkt->tcp);
- #endif
- if (eth_send(htons(pkt->in.length)) == 0)
- return (0); /* sending failed */
- sendtotlen += senddatalen;
- } /* do next ip pkt */
-
- if (s->karn_count == KARN_TIMEOUT)
- {
- if (s->rto) s->rto = (s->rto * 3) / 2;
- else s->rto = normal_waittime;
- }
- else
- {
- s->vj_last = set_ttimeout(0);
- s->karn_count = KARN_NEW;
- }
- s->rtt_time = set_ttimeout(s->rto);
- return (1); /* success */
- }
- /*
- * Format and send a reset tcp packet
- */
- int
- tcp_rst(in_Header *his_ip, tcp_Header *oldtcpp)
- {
- tcp_PseudoHeader ph;
- struct pkt
- {
- in_Header in;
- tcp_Header tcp;
- word maxsegopt[2];
- } register *pkt;
-
- eth_Header *eth;
- register int sendtotlen; /* length of packet */
-
- if (his_ip == NULL || oldtcpp == NULL) return (0); /* failure */
-
- /* see RFC 793 page 65 for details */
- oldtcpp->flags = ntohs(oldtcpp->flags); /* net to local */
-
- if (oldtcpp->flags & tcp_FlagRST)
- return (0);
- if ((oldtcpp->flags & tcp_FlagACK) == 0)
- oldtcpp->flags = tcp_FlagACK;
- else
- oldtcpp->flags = 0;
-
- /* reads his Ethernet address or garbage */
- eth = eth_hardware((byte *)his_ip);
-
- pkt = (struct pkt *)eth_formatpacket(eth, TYPE_IP);
- sendtotlen = sizeof(tcp_Header) + sizeof(in_Header);
- pkt->in.length = htons(sendtotlen);
- /* TCP header */
- pkt->tcp.srcPort = oldtcpp->dstPort;
- pkt->tcp.dstPort = oldtcpp->srcPort;
- pkt->tcp.seqnum = oldtcpp->acknum;
- pkt->tcp.acknum = htonl(ntohl(oldtcpp->seqnum) + 1);
- pkt->tcp.window = 0;
- pkt->tcp.flags = htons(tcp_FlagRST | oldtcpp->flags | 0x5000);
- pkt->tcp.checksum = 0;
- pkt->tcp.urgentPointer = 0;
- /* Internet header */
- pkt->in.hdrlen_ver = 0x45; /* version 4, hdrlen 5 */
- pkt->in.tos = 0;
- pkt->in.identification = htons(++ip_id); /* use pre-inc here */
- pkt->in.frag = 0;
- #ifdef KERMIT
- pkt->in.ttl = 60; /* time to live */
- #else
- pkt->in.ttl = 254;
- #endif
- pkt->in.proto = TCP_PROTO;
- pkt->in.checksum = 0;
- pkt->in.source = his_ip->destination;
- pkt->in.destination = his_ip->source;
- pkt->in.checksum = ~checksum(&pkt->in, sizeof(in_Header));
- /* compute TCP checksum */
- ph.src = pkt->in.source; /* already big endian */
- ph.dst = pkt->in.destination;
- ph.mbz = 0;
- ph.protocol = TCP_PROTO;
- ph.length = htons(sendtotlen - sizeof(in_Header));
- ph.checksum = checksum(&pkt->tcp, (sendtotlen - sizeof(in_Header) + 1)
- & 0xfffe);
- pkt->tcp.checksum = ~checksum(&ph, sizeof(ph));
-
- #ifdef DEBUG
- if (dbugxmit) (*dbugxmit)(NULL, &pkt->in, &pkt->tcp);
- #endif
- return (eth_send(htons(pkt->in.length)));
- }
-
- /* Handle ICMP Redirects. ICMP processor yields new gateway IP number
- in "gateway" and pointer to offending IP header which we have sent.
- Find socket with destination IP address matching the gateway
- sending us the Redirect message, replace the socket's destination
- Ethernet address with that of the new gateway (use arp_resolve).
- For TCP resend the packet, for UDP give up all hope.
- */
- void
- do_redirect(longword gateway, in_Header *ip)
- {
- register udp_Socket *s_udp;
- register tcp_Socket *s_tcp;
- tcp_Header *tp;
- int len;
- longword host;
-
- len = in_GetHdrlenBytes(ip);
- tp = (tcp_Header *)((byte *)ip + len); /* tcp frame pointer */
- host = ntohl(ip->destination); /* destination IP */
-
- switch(ip->proto)
- {
- case TCP_PROTO: /* active TCP sockets */
- for (s_tcp = tcp_allsocs; s_tcp != NULL; s_tcp = s_tcp->next)
- if ((host == s_tcp->hisaddr) && /* if same dest IP */
- (ntohs(tp->srcPort) == s_tcp->myport))
- /* & port */
- {
- arp_resolve(gateway, &s_tcp->hisethaddr[0]);
- tcp_send(s_tcp); /* resend the packet */
- }
- break;
- case UDP_PROTO: /* active UDP sockets */
- for (s_udp = udp_allsocs; s_udp != NULL; s_udp = s_udp->next)
- if ((host == s_udp->hisaddr) &&
- (ntohs(tp->srcPort) == s_udp->myport))
- arp_resolve(gateway, &s_udp->hisethaddr[0]);
- break;
- }
- }
-
- /**********************************************************************
- * socket functions
- **********************************************************************/
-
- /* socket based procedures */
-
- /*
- * sock_mode - set binary or ascii - affects sock_gets, sock_dataready
- * - set udp checksums
- */
- int
- sock_mode(tcp_Socket *s, word mode)
- {
- if (s != NULL)
- {
- s->soc_mode = (s->soc_mode & 0xfffc) | mode;
- return (1); /* success */
- }
- else
- return (0); /* failure */
- }
-
- /*
- * sock_read - read a socket with maximum n bytes
- * - returns count also when connection gets closed
- */
- int
- sock_read(sock_type *s, byte FAR *dp, int len)
- {
- register int templen, count;
- count = 0;
-
- if (s == NULL || dp == NULL) return (0); /* failure */
- do {
- switch (s->tcp.ip_type)
- {
- case UDP_PROTO:
- templen = udp_read((udp_Socket *)s, dp, len);
- break;
- case TCP_PROTO:
- templen = tcp_read((tcp_Socket *)s, dp, len);
- if (templen == 0 &&
- s->tcp.sisopen == SOCKET_CLOSED)
- return (count); /*quit early*/
- break;
- default: return (0);
- }
- count += templen;
- len -= templen;
- }
- while (len > 0);
- return (count);
- }
-
- /*
- * sock_fastread - read a socket with maximum n bytes
- * - does not busywait until buffer is full
- */
- int
- sock_fastread(sock_type *s, byte FAR *dp, int len)
- {
- if (s == NULL) return (0); /* failure */
- switch(s->tcp.ip_type)
- {
- case UDP_PROTO:
- return (udp_read((udp_Socket *)s, dp, len));
- case TCP_PROTO:
- return (tcp_read((tcp_Socket *)s, dp, len));
- default: return (0);
- }
- }
-
-
- /*
- * sock_write - writes data and returns length written
- * - does not perform flush
- */
- int
- sock_write(sock_type *s, byte FAR *dp, int len)
- {
- register int offset, oldlen;
-
- oldlen = len;
- offset = 0;
- if (s == NULL || dp == NULL) return (0); /* failure */
- while (len > 0)
- {
- switch (s->tcp.ip_type)
- {
- case UDP_PROTO:
- offset +=
- udp_write((udp_Socket *)s, &dp[offset], len);
- break;
- case TCP_PROTO:
- offset +=
- tcp_write((tcp_Socket *)s, &dp[offset], len);
- break;
- default: return (0);
- }
- len = oldlen - offset;
- if (tcp_tick(s) == SOCKET_CLOSED)
- return (0); /* no socket */
- }
- return (oldlen);
- }
-
- int
- sock_fastwrite(sock_type *s, byte FAR *dp, int len)
- {
- tcp_tick(NULL); /* updates our output buffer*/
- if (s == NULL) return (0); /* failure */
-
- switch (s->tcp.ip_type)
- {
- case UDP_PROTO:
- return (udp_write((udp_Socket *)s, dp, len));
- case TCP_PROTO:
- return (tcp_write((tcp_Socket *)s, dp, len));
- default: return (0);
- }
- }
-
- int
- sock_flush(sock_type *s)
- {
- if (s == NULL) return (0); /* failure */
- if (s->tcp.ip_type == TCP_PROTO)
- return (tcp_flush((tcp_Socket *)s));
- return (1);
- }
- /*
- * sock_flushnext - cause next transmission to have a flush
- */
- int
- sock_flushnext(sock_type *s)
- {
- if (s == NULL) return (0); /* failure */
- if (s->tcp.ip_type == TCP_PROTO)
- s->tcp.flags |= tcp_FlagPUSH;
- return (1); /* success */
- }
-
- /*
- * sock_putc - put a character
- * - no expansion but flushes on '\n'
- * - returns character
- */
- int
- sock_putc(sock_type *s, byte c)
- {
- byte ch;
-
- ch = c;
- if (s == NULL) return (-1); /* failure */
- if ((ch == '\n') || (ch == '\r'))
- sock_flushnext(s);
- sock_write(s, &ch, 1);
- return (ch & 0xff);
- }
-
- int
- sock_getc(sock_type *s)
- {
- byte ch;
-
- if (s == NULL) return (-1); /* failure */
- sock_read(s, &ch, 1);
- return (ch & 0xff);
- }
-
- #ifndef KERMIT
- /*
- * sock_puts - does not append carriage return in binary mode
- * - returns length
- */
- int
- sock_puts(sock_type *s, byte *dp)
- {
- register int len;
-
- if (s == NULL || dp == NULL) return (0); /* failure */
-
- len = strlen(dp);
- sock_flushnext(s);
- sock_write(s, dp, len);
- if (s->tcp.soc_mode & TCP_MODE_ASCII)
- sock_putc(s, '\n');
- return (len);
- }
- #endif /* KERMIT */
-
- /*
- * sock_update - update the socket window size to the other guy
- * Note: a better and safer criterion is s->rdatalen < mss
- * which allows room for a full incoming segment without loss or overflow.
- */
- static int
- sock_update(tcp_Socket *s)
- {
- if (s == NULL) return (0); /* failure */
- if (s->ip_type == TCP_PROTO)
- {
- if (s->rdatalen < s->rmaxdatalen >> 2)
- tcp_send(s); /* update the window */
- else
- tcp_sendsoon(s);
- }
- return (1); /* success */
- }
-
- /*
- * sock_dataready - returns number of bytes waiting to be read
- * - if in ASCII mode, return 0 until a line is present
- * or the buffer is full
- */
- word
- sock_dataready(sock_type *s)
- {
- register int len;
- byte FAR *p;
-
- if (s == NULL)
- return (0);
-
- if (s->tcp.soc_mode & TCP_MODE_ASCII)
- {
- if (len == TCP_RBUFSIZE)
- return (len);
- /* check for terminating \n \r */
- p = s->tcp.rdata;
- if ((strchrf(p, '\n') == NULL) || (strchrf(p, '\r') == NULL))
- return (len);
- return (0);
- }
- return (s->tcp.rdatalen);
- }
-
- int
- sock_established(sock_type *s)
- {
- if (s == NULL) return (0); /* failure */
- switch (s->tcp.ip_type)
- {
- case UDP_PROTO:
- return (1);
- case TCP_PROTO:
- return (s->tcp.state == tcp_StateESTAB);
- default:
- return (0);
- }
- }
-
- int
- sock_close(sock_type *s)
- {
- register int status;
-
- if (s == NULL) return (0); /* failure */
- switch (s->tcp.ip_type)
- {
- case UDP_PROTO:
- status = udp_close((udp_Socket *)s);
- break;
- case TCP_PROTO:
- status = tcp_close((tcp_Socket *)s);
- tcp_tick(s);
- break;
- default:
- return (0);
- }
- return (status); /* success */
- }
-
- void
- sock_abort(sock_type *s)
- {
- if (s == NULL) return; /* do nothing */
- switch (s->tcp.ip_type)
- {
- case TCP_PROTO:
- tcp_abort((tcp_Socket *)s);
- break;
- case UDP_PROTO:
- udp_close((udp_Socket *)s);
- break;
- }
- }
-
- /*
- * ip user level timer stuff
- * void ip_timer_init(void *s, int delayseconds)
- * int ip_timer_expired(void *s)
- * - 0 if not expired
- */
-
-
- void
- ip_timer_init(sock_type *s, int delayseconds)
- {
- if (s == NULL)
- return;
- if (delayseconds != 0)
- s->udp.usertimer = set_timeout(delayseconds);
- else
- s->udp.usertimer = 0;
- }
-
- int
- ip_timer_expired(sock_type *s)
- {
- if (s == NULL) return (1); /* say time out */
- if (s->udp.usertimer == 0) /* cannot expire */
- return (0);
-
- return (chk_timeout(s->udp.usertimer));
- }
-
- long
- make_timeout(word timeout)
- {
- if (timeout) return (set_timeout(timeout));
- return (0);
- }
-
- /*
- * check_timeout - test agains timeout clock - account for overflow
- */
- int
- check_timeout(unsigned long timeout)
- {
- if (timeout) return (chk_timeout(timeout));
- return (0);
- }
-
- /*
- * ip_delay0 called by macro sock_wait_established()
- * ip_delay1 called by macro sock_wait_intput()
- * ip_delay2 called by macro sock_wait_closed();
- *
- */
-
- ip_delay0(sock_type *s, int timeoutseconds, procref fn, int *statusptr)
- {
- register int status;
-
- if (s == NULL)
- {
- status = -1; /* failure */
- if (statusptr != NULL) *statusptr = status;
- return (status);
- }
- ip_timer_init(s, timeoutseconds);
-
- do
- {
- if (s->tcp.ip_type == TCP_PROTO)
- if (tcp_established((tcp_Socket *)s))
- {
- status = 0;
- break;
- }
- if (tcp_tick(s) == SOCKET_CLOSED)
- {
- s->tcp.err_msg = "Host refused connection";
- status = -1; /* get an early reset */
- break;
- }
-
- if (ip_timer_expired(s))
- {
- sock_close(s);
- status = -1;
- break;
- }
- if (fn != NULL)
- if (status = fn(s, NULL, 0)) break;
- if (s->tcp.ip_type == UDP_PROTO)
- {
- status = 0;
- break;
- }
- } while (1);
- if (statusptr != NULL) *statusptr = status;
- return (status);
- }
-
- int
- ip_delay1(sock_type *s, int timeoutseconds, procref fn, int *statusptr)
- {
- register int status;
-
- if (s == NULL)
- {
- status = -1; /* failure */
- if (statusptr != NULL) *statusptr = status;
- return (status);
- }
-
- ip_timer_init(s, timeoutseconds);
- sock_flush(s); /* new enhancement */
-
- do
- {
- if (sock_dataready(s))
- {
- status = 0;
- break;
- }
-
- if (tcp_tick(s) == SOCKET_CLOSED)
- {
- status = 1;
- break;
- }
- if (ip_timer_expired(s))
- {
- sock_close(s);
- status = -1;
- break;
- }
- if (fn != NULL)
- if (status = fn(s, NULL, 0)) break;
- } while (1 == 1);
-
- if (statusptr != NULL) *statusptr = status;
- return (status);
- }
-
- int
- ip_delay2(sock_type *s, int timeoutseconds, procref fn, int *statusptr)
- {
- register int status;
-
- if (s == NULL)
- {
- status = 0; /* failure */
- if (statusptr != NULL) *statusptr = status;
- return (status);
- }
-
- ip_timer_init(s, timeoutseconds);
-
- if (s->tcp.ip_type != TCP_PROTO) return (1);
-
- do
- {
- if (tcp_tick(s) == SOCKET_CLOSED) /* no socket */
- {
- status = 1;
- break;
- }
- if (ip_timer_expired(s))
- {
- sock_abort(s);
- status = 0;
- break;
- }
- if (fn != NULL)
- if (status = fn(s, NULL, 0)) break;
- } while (1 == 1);
-
- if (statusptr != NULL) *statusptr = status;
- return (status);
- }
-